package com.push_label.config;

import com.push_label.annotations.DisableNestLoop;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;

import java.io.IOException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;


@Intercepts({
        @Signature(
                type =  StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )
})
public class NestLoopControlInterceptor implements Interceptor {

    private static final Set<String> disableNestLoopCache = new HashSet<>();

    public void init()throws ClassNotFoundException, IOException{
        scanAnCache();
    }

    private void scanAnCache() throws ClassNotFoundException, IOException{
        ClassPathScanningCandidateComponentProvider scanner
                = new ClassPathScanningCandidateComponentProvider(false){
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return beanDefinition.getMetadata().isIndependent();
            }
        };
        scanner.addIncludeFilter(((metadataReader, metadataReaderFactory) -> true));
        Set<BeanDefinition> candidateComponents =
                scanner.findCandidateComponents("com.push_label.mapper");

        for (BeanDefinition bd : candidateComponents) {
            Class<?> clazz = Class.forName(bd.getBeanClassName());

            if (!clazz.isInterface()) {
                continue;
            }

            for (Method method : clazz.getDeclaredMethods()) {
                if (method.isAnnotationPresent(DisableNestLoop.class)){
                    String methodId = clazz.getName() + "." + method.getName();
                    disableNestLoopCache.add(methodId);
                }
            }
        }


    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

        String mapperId = ms.getId();
        if (disableNestLoopCache.contains(mapperId)){
            Connection connection = (Connection) invocation.getArgs()[0];
            try (Statement statement =  connection.createStatement()){
                statement.execute("SET LOCAL enable_nestloop  = off");
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
